package com.lahuan.config.datasouce;

import com.lahuan.config.datasouce.entity.*;
import com.mysql.cj.jdbc.result.UpdatableResultSet;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;

/**
 * @author lfy
 */
@Aspect
@Component
public class AopTransactionManager {


    /**
     * 环绕增强
     * @param pjd
     * @param transaction
     * @return
     * @throws Throwable
     */
    @Around("@annotation(transaction)")
    public Object around(ProceedingJoinPoint pjd, CustomTransaction transaction) throws Throwable {
        //是否是开启全局事务的方法
        boolean isStartTransactionMethod = false;
        try {
            Object[] args = pjd.getArgs();
            Signature s = pjd.getSignature();
            String methodName = s.getName();

            GlobalXid globalXid = CustomTransactionHolder.getGlobalXid();
            TransactionBranch branch = initTransactionBranch( pjd,transaction, args);
            if(globalXid==null){
                isStartTransactionMethod = true;
                globalXid = genGlobalXid();
                CustomTransactionHolder.setGlobalXid(globalXid);
                CustomTransactionHolder.setGlobalTransactionState(GlobalTransactionState.exec);
            }
            String branchXid = genBranchXid(methodName, globalXid);
            branch.setBranchXid(branchXid);
            branch.setGlobalXid(globalXid);
            CustomTransactionHolder.addTransactionBranchSet(branch);
            if(isStartTransactionMethod){
                System.out.println(methodName+"开启一个全局事务,branchXid:"+branchXid);
            }else{
                System.out.println(methodName+"加入一个全局事务,branchXid:"+branchXid);
            }
            //获取前一个分支,执行完自己的事务分支后恢复状态
            TransactionBranch prevBranch = CustomTransactionHolder.getCurrentTransactionBranch();
            CustomTransactionHolder.setCurrentTransactionBranch(branch);
            Object result = pjd.proceed(args);
            //恢复
            CustomTransactionHolder.setCurrentTransactionBranch(prevBranch);
            if(isStartTransactionMethod){
                //开启全局事务的方法的代理才进入这里
                //检查是否出现异常
                CustomTransactionHolder.setGlobalTransactionState(GlobalTransactionState.decision);
                System.out.println("全局事务运行完成,进入决策阶段,开始检测是否异常");
                if(CustomTransactionHolder.getTransactionException()){
                    //出现过异常就回滚
                    System.out.println("全局事务决策阶段,发现执行过程中发现异常,执行回滚");
                    try {
                        transactionRollback();
                    }catch (Exception ex){
                        //这里不抛出，不影响原有执行结果
                        System.out.println("执行全局事务回滚出现异常");
                        ex.printStackTrace();
                    }
                }else{
                    System.out.println("全局事务成功,执行过程中未发现异常,");
                }
            }
            return result;
        } catch (Throwable throwable) {
            //出现异常直接回滚
            CustomTransactionHolder.setTransactionException();
            if(isStartTransactionMethod ){
                CustomTransactionHolder.setGlobalTransactionState(GlobalTransactionState.decision);
                System.out.println("执行全局事务期间出现异常,进入决策阶段,执行回滚操作,globalXid:"+CustomTransactionHolder.getGlobalXid());
                transactionRollback();
            }
            throw throwable;
        }finally {
            try {
                if(isStartTransactionMethod ){
                    System.out.println("清空事务上下文");
                    CustomTransactionHolder.clear();
                }
            }catch (Exception ex) {
                System.out.println("清空事务上下文出现异常");
                ex.printStackTrace();
            }
        }

    }





    /**
     * 把所有的事务都回滚
     */
    private void transactionRollback() throws Exception {
        HashSet<TransactionBranch> set = CustomTransactionHolder.getTransactionBranchSet();
        if(set ==null || set.size()==0){
            return ;
        }
        for (TransactionBranch branch : set) {
            List<CustomSqlParserResult> parserResults = branch.getCustomSqlParserResult();
            if(parserResults !=null && parserResults.size()>0){
                for (CustomSqlParserResult parserResult : parserResults) {
                    if(parserResult instanceof InsertCustomSqlParserResult){
                        //生成删除语句
                        InsertCustomSqlParserResult result = (InsertCustomSqlParserResult) parserResult;
                        ColumnValuePair idColumnValue = result.getIdColumnValue();
                        if(idColumnValue ==null){
                            //新增失败时
                            continue;
                        }
                        insertRollback(result, idColumnValue);
                    }
                    if(parserResult instanceof UpdateCustomSqlParserResult){
                        UpdateCustomSqlParserResult result = (UpdateCustomSqlParserResult) parserResult;
                        updateRollback(result);
                    }
                    if(parserResult instanceof DeleteCustomSqlParserResult){
                        DeleteCustomSqlParserResult result = (DeleteCustomSqlParserResult) parserResult;
                        deleteRollback(result);
                    }
                }
            }
        }
        System.out.println("全局事务回滚全部执行完成");
    }

    private void deleteRollback(DeleteCustomSqlParserResult result) throws SQLException {
        String tableName = result.getTableName();
        List<ColumnValuePair> beforeColumnValue = result.getBeforeColumnValue();
        if(beforeColumnValue==null){
            //删除不存在的数据时
            return ;
        }
        String totalColumnStr="";
        String totalValueStr="";
        for (int i = 0; i < beforeColumnValue.size(); i++) {
            ColumnValuePair pair = beforeColumnValue.get(i);
            String column = pair.getColumn();
            String valueStr = "null";
            Object value = pair.getValue();
            if(value != null){
                valueStr = value.toString();
            }
            totalColumnStr += " "+ column + " ";
            totalValueStr += " "+ valueStr + " ";
            //非最后一个字段
            if(i != beforeColumnValue.size() -1){
                totalColumnStr+=",";
                totalValueStr+=",";
            }
        }
        String reverseSql = "insert into " + tableName + " ( "+totalColumnStr +" ) values ( " +totalValueStr+" ); ";
        String dataSourceName = result.getDataSourceName();
        System.out.println("生成回滚语句->"+dataSourceName +":"+reverseSql);
        DataSource dataSource = result.getDataSource();
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(reverseSql);
        statement.execute();
        statement.close();
        connection.close();
    }

    private void updateRollback(UpdateCustomSqlParserResult result) throws SQLException {
        ColumnValuePair idColumnValue = result.getIdColumnValue();
        String tableName = result.getTableName();

        List<ColumnValuePair> beforeColumnValue = result.getBeforeColumnValue();
        String columnId = idColumnValue.getColumn();
        Object valueId = idColumnValue.getValue();
        String reverseSql = "update " + tableName + " set ";
        if(beforeColumnValue==null){
            //修改不存在的数据时
            return ;
        }
        for (int i = 0; i < beforeColumnValue.size(); i++) {
            ColumnValuePair pair = beforeColumnValue.get(i);
            String column = pair.getColumn();
            String valueStr = "null";
            Object value = pair.getValue();
            if(value != null){
                valueStr = value.toString();
            }
            reverseSql += column + " = " + valueStr;
            //非最后一个字段
            if(i != beforeColumnValue.size() -1){
                reverseSql+=",";
            }
        }
        reverseSql +=  " where " + columnId +" = " + valueId + " ;";
        String dataSourceName = result.getDataSourceName();
        System.out.println("生成回滚语句->"+dataSourceName +":"+reverseSql);
        DataSource dataSource = result.getDataSource();
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(reverseSql);
        statement.execute();
        statement.close();
        connection.close();
    }

    private void insertRollback(InsertCustomSqlParserResult result, ColumnValuePair idColumnValue) throws SQLException {
        String tableName = result.getTableName();
        String column = idColumnValue.getColumn();
        Object value = idColumnValue.getValue();
        String reverseSql = "delete from " + tableName +  " where " + column +" = " + value +" ;";
        String dataSourceName = result.getDataSourceName();
        System.out.println("生成回滚语句->"+dataSourceName +":"+reverseSql);
        DataSource dataSource = result.getDataSource();
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(reverseSql);
        statement.execute();
        statement.close();
        connection.close();
    }


    /**
     * 初始化分支事务信息
     *
     * @param pjd
     * @param transaction
     * @param args
     * @return
     */
    private TransactionBranch initTransactionBranch(ProceedingJoinPoint pjd, CustomTransaction transaction, Object[] args) {
        TransactionBranch branch = new TransactionBranch();
        branch.setCustomSqlParserResult(new ArrayList<>());
        return branch;
    }
    /**
     * 生成分支事务id
     * @param signature
     * @param globalXid
     * @return
     */
    private String genBranchXid(String signature, GlobalXid globalXid) {
        return signature+"-"+globalXid.getXid() +"-"+ UUID.randomUUID().toString();
    }


    /**
     * 生成全局事务id
     * @return
     */
    private GlobalXid genGlobalXid() {
        //xid生成，这里用UUID来简化，实际环境需要保证唯一
        GlobalXid globalXid = new GlobalXid();
        globalXid.setXid(UUID.randomUUID().toString());
        return globalXid;
    }




}
